#ifndef SCENE_HPP
#define SCENE_HPP
#include <list>
#include "algebra.hpp"
#include "primitive.hpp"
#include "material.hpp"
#include "mesh.hpp"
#include "image.hpp"

class SceneNode {
public:
	SceneNode();
  SceneNode(const std::string& name);
  virtual ~SceneNode();
  virtual bool ray_intersect(Ray ray, Intersection *intersection);
  const Matrix4x4& get_transform() const { return m_trans; }
  const Matrix4x4& get_inverse() const { return m_invtrans; }
  
	virtual std::pair<Point3D,Point3D> get_voxels(){
		return std::pair<Point3D,Point3D>(Point3D(0,0,0),Point3D(0,0,0));
	}
  void set_transform(const Matrix4x4& m)
  {
    m_trans = m;
    m_invtrans = m.invert();
  }

  void set_transform(const Matrix4x4& m, const Matrix4x4& i)
  {
    m_trans = m;
    m_invtrans = i;
  }

  void add_child(SceneNode* child)
  {
    m_children.push_back(child);
  }

  void remove_child(SceneNode* child)
  {
    m_children.remove(child);
  }

  // Callbacks to be implemented.
  // These will be called from Lua.
  void rotate(char axis, double angle);
  void scale(const Vector3D& amount);
  void translate(const Vector3D& amount);

  // Returns true if and only if this node is a JointNode
  virtual bool is_joint() const;
virtual bool is_geometry() const;
virtual void flat_hier(const Matrix4x4& trans);
std::string get_name() { return m_name;}
protected:
  
  // Useful for picking
  int m_id;
  std::string m_name;

  // Transformations
  Matrix4x4 m_trans;
  Matrix4x4 m_invtrans;

  // Hierarchy
  typedef std::list<SceneNode*> ChildList;
  ChildList m_children;
};

class JointNode : public SceneNode {
public:
  JointNode(const std::string& name);
  virtual ~JointNode();

  virtual bool is_joint() const;
  void set_joint_x(double min, double init, double max);
  void set_joint_y(double min, double init, double max);
  struct JointRange {
    double min, init, max;
  };

  
protected:

  JointRange m_joint_x, m_joint_y;
};

class GeometryNode : public SceneNode {
public:
  GeometryNode(const std::string& name,
               Primitive* primitive);
  virtual ~GeometryNode();
  const Material* get_material() const;
  Material* get_material();
  void set_material(Material* material)
  {
	m_material = material;
  }
	void set_texture(Texture* texture)
	{
		m_texture = texture;
	}
	void set_bump(Bump* bump)
	{
		m_bump = bump;
	}
	virtual bool ray_intersect(Ray ray, Intersection *intersection);
	virtual bool is_geometry() const;
	virtual void flat_hier(const Matrix4x4& trans);
	virtual std::pair<Point3D,Point3D> get_voxels()
	{
		std::pair<Point3D,Point3D> temp = m_primitive->get_voxels();
		Point3D p1 = m_trans * temp.first;
		Point3D p2 = m_trans * temp.second;
		return std::pair<Point3D,Point3D>(p1,p2); 
	}
	int get_id(){ return m_id; }
protected:
  Material* m_material;
  Primitive* m_primitive;
	Texture* m_texture;
	Bump* m_bump;
};

class CSGNode : public SceneNode {
public:
	CSGNode(const std::string& name, int operation, SceneNode* node1, SceneNode* node2);
	virtual ~CSGNode();
	virtual bool ray_intersect(Ray ray, Intersection *intersection);
	virtual void flat_hier(const Matrix4x4& trans);
	virtual std::pair<Point3D,Point3D> get_voxels()
	{
		std::pair<Point3D,Point3D> temp1 = m_node1->get_voxels();
		std::pair<Point3D,Point3D> temp2 = m_node2->get_voxels();
		Point3D p1 = m_trans * temp1.first;
		Point3D p2 = m_trans * temp1.second;
		Point3D P1 = m_trans * temp2.first;
		Point3D P2 = m_trans * temp2.second;
		double x1,y1,z1,x2,y2,z2;
		x1 = p1[0] < P1[0]? p1[0]:P1[0];
		y1 = p1[1] < P1[1]? p1[1]:P1[1];
		z1 = p1[2] < P1[2]? p1[2]:P1[2];
		x2 = p2[0] > P2[0]? p2[0]:P2[0];
		y2 = p2[1] > P2[1]? p2[1]:P2[1];
		z2 = p2[2] > P2[2]? p2[2]:P2[2];
		return std::pair<Point3D,Point3D>(Point3D(x1,y1,z1),Point3D(x2,y2,z2)); 
	}
private:
	int m_operation;
	SceneNode* m_node1;
	SceneNode* m_node2;
};
#endif
